package com.thwj.project.app.service;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.thwj.common.constant.HttpStatus;
import com.thwj.common.exception.ServiceException;
import com.thwj.common.utils.StringUtils;
import com.thwj.framework.security.LoginUser;
import com.thwj.framework.web.service.TokenService;
import com.thwj.project.app.api.param.AuthRequest;
import com.thwj.project.biz.domain.User;
import com.thwj.project.biz.enums.UserStatusEnum;
import com.thwj.project.biz.service.IUserService;
import com.thwj.project.system.domain.SysUser;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashSet;

@Slf4j
@Transactional
@Service
public class WxAuthService {

    @Autowired
    private WxMaService wxMaService;
    @Autowired
    private IUserService userService;
    @Autowired
    private TokenService tokenService;

    public String wxSmallAppLogin(AuthRequest authRequest) {
        if(authRequest.getCode() == null) {
            throw new ServiceException("缺少参数");
        }
        // 1：根据code获取微信openid信息
        WxMaJscode2SessionResult sessionResult;
        try {
            sessionResult = wxMaService.getUserService().getSessionInfo(authRequest.getCode());
        }catch (WxErrorException e) {
            log.error("createAuthenticationToken=>code2openid error,reason :{}",e);
            throw new ServiceException("微信session解析异常");
        }
        // 2: 尝试获取手机号
        WxMaPhoneNumberInfo wxMaPhoneNumberInfo = null;
        if (!StringUtils.isEmpty(authRequest.getPhoneCode())) {
            try {
                wxMaPhoneNumberInfo = wxMaService.getUserService().getPhoneNoInfo(authRequest.getPhoneCode());
            }catch (WxErrorException e) {
                log.error("createAuthenticationToken=>code2openid error,reason :{}",e);
                throw new ServiceException("微信手机号解析异常");
            }
        }
        // 3：查询数据库是否存在该用户
        User user = userService.getOne(new QueryWrapper<User>().eq("mini_openid",sessionResult.getOpenid()).last("limit 1"));
//        User user = userService.getOne(new QueryWrapper<User>().eq("unionId",sessionResult.getUnionid()).last("limit 1"));
        // 新增一个事务更新最新的开发平台uniodid
        if(user!= null && StringUtils.isEmpty(user.getUnionid())) {
            this.updateNewUnionid(user, sessionResult.getUnionid());
        }
        if(user != null && UserStatusEnum.forbidden.getValue() == user.getStatus()) {
            throw new ServiceException("当前用户已冻结，请联系客服");
        }

        //1：不存在该用户或者手机号没有授权 需要前端唤起授权手机号 这里注意 两个code应该都需要重新获取最新的值，否则解析异常
        if ((user == null || StringUtils.isEmpty(user.getPhone())) && wxMaPhoneNumberInfo == null) {
            throw new ServiceException("用户需授权手机号", HttpStatus.USER_NO_PHONE);
        }
        //2 用户已存在&手机号以获取 直接构建token返回
        if (user != null && StringUtils.isNotEmpty(user.getPhone())) {
            return getTokenByUser(user);
        }
        // 3: 可能已经公众号关注了 产生了用户但是 小程序没有授权
        if (user != null && StringUtils.isEmpty(user.getMiniOpenid()) && StringUtils.isEmpty(user.getPhone())) {
            // 更新当前用户信息
            user.setPhone(wxMaPhoneNumberInfo.getPhoneNumber());
            user.setMiniOpenid(sessionResult.getOpenid());
            user.setModifyDate(new Date());
            userService.updateById(user);
            // 构建返回数据
            return getTokenByUser(user);
        }

        // 新建用户 同时保存用户授权的手机号
        User newUser = new User();
        newUser.setPhone(wxMaPhoneNumberInfo.getPhoneNumber());
        newUser.setMiniOpenid(sessionResult.getOpenid());
        // getUnionid 这个是需要在开放平台绑定这样就能实现公众号的用户和小程序的用户唯一关系
        newUser.setUnionid(sessionResult.getUnionid());
        // 默认值
        newUser.setStatus(UserStatusEnum.normal.getValue());
        newUser.setCreateDate(new Date());
        newUser.setModifyDate(new Date());
        userService.save(newUser);
        // 构建返回数据
        return getTokenByUser(newUser);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateNewUnionid(User user, String unionid) {
        user.setUnionid(unionid);
        // 查看有没有先关注公众号
        User userMp = userService.getOne(new QueryWrapper<User>()
                .eq("unionId", unionid)
                .isNotNull("mp_openid")
                .last("limit 1"));
        // 先关注的公众号
        if(userMp != null) {
            user.setMpOpenid(userMp.getMpOpenid());
        }
        if(userMp!= null) {
            userService.removeById(userMp.getId());
        }
        userService.updateById(user);
        log.info("updateNewUnionid: {}", user);

    }

    private String getTokenByUser(User user) {
        LoginUser tokenUser = new LoginUser(new SysUser(user.getId()), new HashSet<>());
        tokenUser.setUserId(user.getId());
        Authentication authentication = new UsernamePasswordAuthenticationToken(tokenUser, null,
                AuthorityUtils.createAuthorityList("WX_APP_USER"));
        SecurityContextHolder.getContext().setAuthentication(authentication);
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        return tokenService.createToken(loginUser);
    }

    public String testCreateToken() {
        User newUser = userService.getById(1L);
        // 构建返回数据
        return getTokenByUser(newUser);
    }
}
